using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Net;
using System.Windows.Threading;
using log4net;
using Microsoft.SharePoint.Client;
using MSDN.Samples.ClaimsAuth;
using SharePoint.Security.ContentScanner.Engine.Helpers;
using SharePoint.Security.DocHelpers.ExportFormats;
using File = Microsoft.SharePoint.Client.File;

namespace SharePoint.Security.ContentScanner.Engine.Analyzers
{
    public static class Helper
    {
        public static readonly List<string> Urls = new List<string>();
        public static string userName = string.Empty;
        public static string password = string.Empty;
        public static string userDomain = string.Empty;

        public static List<SummaryInfo> scanSummary = new List<SummaryInfo>();
        public static List<SummaryInfo> scanPatternSummary = new List<SummaryInfo>();

        public static SiteViewModel scanSitesView = new SiteViewModel {SitePath = "Sites to Scan"};
        public static List<string> unselectedUrls = new List<string>();


        public static string GetPermissions(File spFile, ClientContext clientContext)
        {
            ILog logger = Logger.Log;
            string permissions = string.Empty;
            try
            {
                logger.Info(string.Format("Started fetching the permissions for the File {0} at {1}",
                    spFile.GetFileUrl(), DateTime.Now));
                File file = clientContext.Web.GetFileByServerRelativeUrl(spFile.ServerRelativeUrl);
                clientContext.Load(file);
                clientContext.ExecuteQuery();

                IEnumerable<RoleAssignment> roles;

                roles = clientContext.LoadQuery(
                    file.ListItemAllFields.RoleAssignments.Include(
                        roleAsg => roleAsg.Member,
                        roleAsg => roleAsg.RoleDefinitionBindings.Include(
                            roleDef => roleDef.Name, // for each role def, include roleDef's Name
                            roleDef => roleDef.Description)));

                clientContext.ExecuteQuery();


                foreach (RoleAssignment ra in roles)
                {
                    Principal p = ra.Member;
                    clientContext.Load(p);
                    clientContext.ExecuteQuery();

                    foreach (RoleDefinition roleDef in ra.RoleDefinitionBindings)
                    {
                        if (permissions.Length > 0)
                        {
                            permissions += Environment.NewLine + string.Format("{0} -> {1}.", p.LoginName, roleDef.Name);
                        }
                        else
                        {
                            permissions += string.Format("{0} -> {1}.", p.LoginName, roleDef.Name);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                permissions = string.Format("Can not fetch permissions. Error - {0}", ex.Message);
            }
            finally
            {
                logger.Info(string.Format("Completed fetching the permissions for the File {0} at {1}",
                    spFile.GetFileUrl(), DateTime.Now));
            }

            return permissions;
        }

        public static bool CanScanDocument(string fileName, List<SelectedFileExtension> fileExtns)
        {
            string ext = Path.GetExtension(fileName);
            if (String.IsNullOrEmpty(ext))
            {
                return false;
            }

            switch (ext.ToLower())
            {
                case ".docx":                
                case ".aspx":
                    foreach (SelectedFileExtension fileExtn in fileExtns)
                    {
                        if (fileExtn.FileExtension.Equals(ext, StringComparison.OrdinalIgnoreCase))
                            return true;
                    }
                    return false;
                default:
                    return false;
            }
        }

        public static ClientContext GetClientContext(string url, string type)
        {
            if (type == "Online")
            {
                return ClaimClientContext.GetAuthenticatedContext(url.Trim());
            }
            var clientContext = new ClientContext(url.Trim());
            if (userName != string.Empty && password != string.Empty && userDomain != string.Empty)
            {
                clientContext.Credentials = new NetworkCredential(userName, password, userDomain);
            }
            return clientContext;
        }

        public static List<string> GetSites(List<string> urls, List<ErrorInfo> errors, string type,
            bool useExistingAuthCookie = true, bool fetchSubSites = true)
        {
            ILog logger = Logger.Log;
            try
            {
                if (type == "Online" && !useExistingAuthCookie)
                {
                    ClaimClientContext.AuthCookie = null;
                }

                foreach (string url in urls)
                {
                    var newModel = new SiteViewModel();
                    newModel.SitePath = url;

                    scanSitesView.Subsites.Add(newModel);

                    GetSites(url, errors, type, newModel, useExistingAuthCookie, fetchSubSites);

                    if (errors.Count > 0)
                    {
                        scanSitesView.Subsites.Clear();
                        Urls.Clear();
                    }
                }

                return Urls;
            }
            catch (ApplicationException aex)
            {
                logger.Error("An application error has occured: " + aex.Message);
                errors.Add(new ErrorInfo("An application error has occured: " + aex.Message));
                throw;
            }
            catch (ArgumentException agex)
            {
                logger.Error("An error has occured: " + agex.Message);
                errors.Add(new ErrorInfo("An error has occured: " + agex.Message));
                throw;
            }
            catch (WebException webex)
            {
                string msg = string.Format("An Error has occured for user authorization. {0}", webex.Message);
                errors.Add(new ErrorInfo(msg));
                throw;
            }
            catch (Exception ex)
            {
                logger.Error("Error while adding to subsites" + ex.Message);
                errors.Add(new ErrorInfo("Error while adding to subsites" + ex.Message));
                return null;
            }
        }

        public static void GetSites(string url, List<ErrorInfo> errors, string type, SiteViewModel parentNode,
            bool useExistingAuthCookie = true, bool fetchSubSites = true)
        {
            using (ClientContext clientContext = GetClientContext(url, type))
            {
                try
                {
                    Urls.Add(url);

                    if (!fetchSubSites)
                        return;

                    Web oWebsite = clientContext.Web;
                    clientContext.Load(oWebsite,
                        website => website.Webs,
                        website => website.Title
                        );
                    clientContext.ExecuteQuery();

                    for (int i = 0; i != oWebsite.Webs.Count; i++)
                    {
                        var subModel = new SiteViewModel();
                        subModel.SitePath = oWebsite.Webs[i].Url;
                        subModel.IsSelected = true;
                        parentNode.Subsites.Add(subModel);
                        try
                        {
                            GetSites(oWebsite.Webs[i].Url, errors, type, subModel);
                        }
                        catch (PropertyOrFieldNotInitializedException)
                        {
                            GetSites(oWebsite.Webs[i].GetUrl(), errors, type, subModel);
                        }
                    }
                }
                catch (NullReferenceException)
                {
                    errors.Add(new ErrorInfo("An application error has occured. Please check the URI provided"));
                    throw;
                }
                catch (Exception ex)
                {
                    string msg = string.Format("Unable to process {0}. Error: {1}", url, ex.Message);
                    var errorInfo = new ErrorInfo(msg);
                    errors.Add(errorInfo);

                    userName = null;
                    userDomain = null;
                    password = null;
                }
            }
        }
    }

    public class SiteViewModel : INotifyPropertyChanged
    {
        private bool _isSelected;

        public SiteViewModel()
        {
            Subsites = new CustomObservableCollection<SiteViewModel>();
            IsSelected = true;
            IsUserEnabled = true;

            //this.Subsites.Add(new SiteViewModela() { SitePath = "rootURL", IsSelected = true });
        }

        public string SitePath { get; set; }

        public bool IsSelected
        {
            get { return _isSelected; }
            set
            {
                _isSelected = value;
                OnPropertyChanged("IsSelected");
            }
        }

        public bool IsUserEnabled { get; set; }
        public CustomObservableCollection<SiteViewModel> Subsites { get; set; }


        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                CustomObservableCollection<SiteViewModel> childern = Subsites;
                foreach (SiteViewModel site in childern)
                {
                    site.IsSelected = IsSelected;
                }


                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }

    public class CustomObservableCollection<T> : ObservableCollection<T>
    {
        public override event NotifyCollectionChangedEventHandler CollectionChanged;

        protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
        {
            NotifyCollectionChangedEventHandler evntHandler = CollectionChanged;
            if (evntHandler != null)
            {
                Dispatcher dispatcher =
                    (from NotifyCollectionChangedEventHandler notifyChangeHandler in evntHandler.GetInvocationList()
                        let dispatchObj = notifyChangeHandler.Target as DispatcherObject
                        where dispatchObj != null
                        select dispatchObj.Dispatcher).FirstOrDefault();

                if (dispatcher != null && dispatcher.CheckAccess() == false)
                {
                    dispatcher.Invoke(DispatcherPriority.DataBind, (Action) (() => OnCollectionChanged(e)));
                }
                else
                {
                    foreach (NotifyCollectionChangedEventHandler notifyChange in evntHandler.GetInvocationList())
                        notifyChange.Invoke(this, e);
                }
            }
        }
    }
}